Enter the name of the tissue you want to analyze.

tissue_of_interest = "Muscle"
library(here)
source(here("00_data_ingest", "02_tissue_analysis_rmd","boilerplate.R"))
tiss <- load_tissue_facs(tissue_of_interest)
Performing log-normalization
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
[1] "Scaling data matrix"

  |                                                          
  |                                                    |   0%
  |                                                          
  |====================================================| 100%
Calculating gene means
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
Calculating gene variance to mean ratios
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|

A sanity check: genes per cell vs reads per cell.

GenePlot(object = tiss, gene1 = "nReads", gene2 = "nGene", use.raw=T)

Filter out cells with few reads and few genes.

tiss <- FilterCells(object = tiss, subset.names = c("nGene", "nReads"), low.thresholds = c(500, 50000))

Normalize the data, then center and scale.

tiss <- NormalizeData(object = tiss, scale.factor = 1e6)
Performing log-normalization
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
tiss <- ScaleData(object = tiss)
[1] "Scaling data matrix"

  |                                                          
  |                                                    |   0%
  |                                                          
  |====================================================| 100%
tiss <- FindVariableGenes(object = tiss, do.plot = TRUE, x.high.cutoff = Inf, y.cutoff = 0.5)
Calculating gene means
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
Calculating gene variance to mean ratios
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|

Run Principal Component Analysis.

tiss <- RunPCA(object = tiss, do.print = FALSE)
tiss <- ProjectPCA(object = tiss, do.print = FALSE)

Later on (in FindClusters and TSNE) you will pick a number of principal components to use. This has the effect of keeping the major directions of variation in the data and, ideally, supressing noise. There is no correct answer to the number to use, but a decent rule of thumb is to go until the plot plateaus.

PCElbowPlot(object = tiss)

Choose the number of principal components to use.

# Set number of principal components. 
n.pcs = 10

The clustering is performed based on a nearest neighbors graph. Cells that have similar expression will be joined together. The Louvain algorithm looks for groups of cells with high modularity–more connections within the group than between groups. The resolution parameter determines the scale. Higher resolution will give more clusters, lower resolution will give fewer.

For the top-level clustering, aim to under-cluster instead of over-cluster. It will be easy to subset groups and further analyze them below.

# Set resolution 
res.used <- 0.5
tiss <- FindClusters(object = tiss, reduction.type = "pca", dims.use = 1:n.pcs, 
    resolution = res.used, print.output = 0, save.SNN = TRUE)
Build parameters exactly match those of already computed and stored SNN. To force recalculation, set force.recalc to TRUE.Clustering parameters for resolution 0.5 exactly match those of already computed. 
  To force recalculation, set force.recalc to TRUE.

We use TSNE solely to visualize the data.

# If cells are too spread out, you can raise the perplexity. If you have few cells, try a lower perplexity (but never less than 10).
tiss <- RunTSNE(object = tiss, dims.use = 1:n.pcs, seed.use = 10, perplexity=30)
TSNEPlot(object = tiss, do.label = T, pt.size = 1.2, label.size = 4)

Color by metadata, like plate barcode, to check for batch effects.

TSNEPlot(object = tiss, do.return = TRUE, group.by = "plate.barcode")

Check expression of genes useful for indicating cell type.

Dotplots let you see the intensity of expression and the fraction of cells expressing for each of your genes of interest. The radius shows you the percent of cells in that cluster with at least one read sequenced from that gene. The color level indicates the average Z-score of gene expression for cells in that cluster, where the scaling is done over all cells in the sample.

We can also find all differentially expressed genes marking each cluster. This may take some time.

#clust.markers0 <- FindMarkers(object = tiss, ident.1 = 0, only.pos = TRUE, min.pct = 0.25, thresh.use = 0.25)
#tiss.markers <- FindAllMarkers(object = tiss, only.pos = TRUE, min.pct = 0.25, thresh.use = 0.25)

Display the top markers you computed above.

#tiss.markers %>% group_by(cluster) %>% top_n(5, avg_diff)

Using the markers above, we can confidentaly label many of the clusters:

We will add those cell_ontology_class to the dataset.

tiss <- StashIdent(object = tiss, save.name = "cluster.ids")
cluster.ids <- c(0, 1, 2, 3, 4, 5, 6)
free_annotation <- c(
  "skeletal muscle satellite cell", 
  "mesenchymal stem cell", 
  "skeletal muscle satellite cell", 
  "endothelial cell",
  "B cell", 
  "macrophage",
  "T cell")
cell_ontology_class <-c(
  "skeletal muscle satellite cell", 
  "mesenchymal stem cell", 
  "skeletal muscle satellite cell", 
  "endothelial cell",
  "B cell", 
  "macrophage",
  "T cell")
validate_cell_ontology(cell_ontology_class)
cell_ontology_id = convert_to_cell_ontology_id(cell_ontology_class)
tiss@meta.data['free_annotation'] <- as.character(plyr::mapvalues(x = tiss@ident, from = cluster.ids, to = free_annotation))
validate_cell_ontology(cell_ontology_class)
cell_ontology_id = convert_to_cell_ontology_id(cell_ontology_class)
tiss@meta.data['cell_ontology_class'] <- as.character(plyr::mapvalues(x = tiss@ident, from = cluster.ids, to = cell_ontology_class))
tiss@meta.data['cell_ontology_id'] <- as.character(plyr::mapvalues(x = tiss@ident, from = cluster.ids, to = cell_ontology_id))

Subcluster

subtiss = SubsetData(tiss, ident.use = c(1))
subtiss <- subtiss %>% ScaleData() %>% 
  FindVariableGenes(do.plot = TRUE, x.high.cutoff = Inf, y.cutoff = 0.5) %>%
  RunPCA(do.print = FALSE)
[1] "Scaling data matrix"

  |                                                          
  |                                                    |   0%
  |                                                          
  |====================================================| 100%
Calculating gene means
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
Calculating gene variance to mean ratios
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|

PCHeatmap(object = subtiss, pc.use = 1:3, cells.use = 500, do.balanced = TRUE, label.columns = FALSE, num.genes = 8)

PCElbowPlot(subtiss)

sub.n.pcs = 10
sub.res.use = 1
subtiss <- subtiss %>% FindClusters(reduction.type = "pca", dims.use = 1:sub.n.pcs, 
    resolution = sub.res.use, print.output = 0, save.SNN = TRUE, force.recalc = TRUE) %>%
    RunTSNE(dims.use = 1:sub.n.pcs, seed.use = 10, perplexity=20)

TSNEPlot(object = subtiss, do.label = T, pt.size = 1.2, label.size = 4)
TSNEPlot(subtiss, group.by = "mouse.id")

table(FetchData(subtiss, c('mouse.id','ident')) %>% droplevels())
        ident
mouse.id   1
  3_10_M  73
  3_11_M  80
  3_38_F 138
  3_39_F 117
  3_8_M   73
  3_9_M   16
# sub.cluster.ids <- c(0, 1, 2, 3, 4, 5, 6, 7)
# sub.free_annotation <- c("pancreatic A cell", "pancreatic A cell", "pancreatic A cell", "pancreatic D cell", "pancreatic A cell", "pancreatic D cell", "pancreatic PP cell", "pancreatic PP cell")
# sub.cell_ontology_class <-c("pancreatic A cell", "pancreatic A cell", "pancreatic A cell", "pancreatic D cell", "pancreatic A cell", "pancreatic D cell", "pancreatic PP cell", "pancreatic PP cell")
# 
# validate_cell_ontology(sub.cell_ontology_class)
# sub.cell_ontology_id = convert_to_cell_ontology_id(sub.cell_ontology_class)
# 
# subtiss@meta.data['free_annotation'] <- as.character(plyr::mapvalues(x = subtiss@ident, from = sub.cluster.ids, to = sub.free_annotation))
# validate_cell_ontology(sub.cell_ontology_class)
# sub.cell_ontology_id = convert_to_cell_ontology_id(sub.cell_ontology_class)
# 
# subtiss@meta.data['free_annotation'] <- as.character(plyr::mapvalues(x = subtiss@ident, from = sub.cluster.ids, to = sub.free_annotation))
# subtiss@meta.data['cell_ontology_class'] <- as.character(plyr::mapvalues(x = subtiss@ident, from = sub.cluster.ids, to = sub.cell_ontology_class))
# subtiss@meta.data['cell_ontology_id'] <- as.character(plyr::mapvalues(x = subtiss@ident, from = sub.cluster.ids, to = sub.cell_ontology_id))

Add subcluster cell_ontology_class to main cell_ontology_class

# sub.cells = rownames(subtiss@meta.data)
# 
# tiss@meta.data[sub.cells, 'free_annotation'] = subtiss@meta.data[,'free_annotation']
# tiss@meta.data[sub.cells, 'cell_ontology_class'] = subtiss@meta.data[,'cell_ontology_class']
# tiss@meta.data[sub.cells, 'cell_ontology_id'] = subtiss@meta.data[,'cell_ontology_id']

Final coloring

Color by cell ontology class on the original TSNE.

TSNEPlot(object = tiss, do.return = TRUE, group.by = "cell_ontology_class")

Save the Robject for later

filename = here('00_data_ingest', '04_tissue_robj_generated', 
                     paste0("facs_", tissue_of_interest, "_seurat_tiss.Robj"))
print(filename)
[1] "/Users/mikewosczyna/Documents/GitHub/tabula-muris/00_data_ingest/04_tissue_robj_generated/facs_Muscle_seurat_tiss.Robj"
save(tiss, file=filename)
# To reload a saved object
# filename = here('00_data_ingest', '04_tissue_robj_generated', 
#                      paste0("facs", tissue_of_interest, "_seurat_tiss.Robj"))
# load(file=filename)

Export the final metadata

So that Biohub can easily combine all your annotations, please export them as a simple csv.

head(tiss@meta.data)
filename = here('00_data_ingest', '03_tissue_annotation_csv', 
                     paste0(tissue_of_interest, "_annotation.csv"))
write.csv(FetchData(tiss, c('plate.barcode','cell_ontology_class','cell_ontology_id', 'free_annotation', 'tSNE_1', 'tSNE_2')), file=filename)

Figures for Supplement. Dot plot above is sufficient and code is copied below.

```

LS0tCnRpdGxlOiAiTXVzY2xlIEZBQ1MgTm90ZWJvb2siCm91dHB1dDogaHRtbF9ub3RlYm9vawotLS0KCkVudGVyIHRoZSBuYW1lIG9mIHRoZSB0aXNzdWUgeW91IHdhbnQgdG8gYW5hbHl6ZS4KCmBgYHtyfQp0aXNzdWVfb2ZfaW50ZXJlc3QgPSAiTXVzY2xlIgpgYGAKCgpgYGB7cn0KbGlicmFyeShoZXJlKQpzb3VyY2UoaGVyZSgiMDBfZGF0YV9pbmdlc3QiLCAiMDJfdGlzc3VlX2FuYWx5c2lzX3JtZCIsImJvaWxlcnBsYXRlLlIiKSkKdGlzcyA8LSBsb2FkX3Rpc3N1ZV9mYWNzKHRpc3N1ZV9vZl9pbnRlcmVzdCkKYGBgCgpBIHNhbml0eSBjaGVjazogZ2VuZXMgcGVyIGNlbGwgdnMgcmVhZHMgcGVyIGNlbGwuCgpgYGB7cn0KR2VuZVBsb3Qob2JqZWN0ID0gdGlzcywgZ2VuZTEgPSAiblJlYWRzIiwgZ2VuZTIgPSAibkdlbmUiLCB1c2UucmF3PVQpCmBgYAoKRmlsdGVyIG91dCBjZWxscyB3aXRoIGZldyByZWFkcyBhbmQgZmV3IGdlbmVzLgoKYGBge3J9CnRpc3MgPC0gRmlsdGVyQ2VsbHMob2JqZWN0ID0gdGlzcywgc3Vic2V0Lm5hbWVzID0gYygibkdlbmUiLCAiblJlYWRzIiksIGxvdy50aHJlc2hvbGRzID0gYyg1MDAsIDUwMDAwKSkKYGBgCgoKTm9ybWFsaXplIHRoZSBkYXRhLCB0aGVuIGNlbnRlciBhbmQgc2NhbGUuCgpgYGB7cn0KdGlzcyA8LSBOb3JtYWxpemVEYXRhKG9iamVjdCA9IHRpc3MsIHNjYWxlLmZhY3RvciA9IDFlNikKdGlzcyA8LSBTY2FsZURhdGEob2JqZWN0ID0gdGlzcykKdGlzcyA8LSBGaW5kVmFyaWFibGVHZW5lcyhvYmplY3QgPSB0aXNzLCBkby5wbG90ID0gVFJVRSwgeC5oaWdoLmN1dG9mZiA9IEluZiwgeS5jdXRvZmYgPSAwLjUpCmBgYAoKClJ1biBQcmluY2lwYWwgQ29tcG9uZW50IEFuYWx5c2lzLgpgYGB7cn0KdGlzcyA8LSBSdW5QQ0Eob2JqZWN0ID0gdGlzcywgZG8ucHJpbnQgPSBGQUxTRSkKdGlzcyA8LSBQcm9qZWN0UENBKG9iamVjdCA9IHRpc3MsIGRvLnByaW50ID0gRkFMU0UpCmBgYAoKYGBge3IsIGVjaG89RkFMU0UsIGZpZy5oZWlnaHQ9NCwgZmlnLndpZHRoPTh9ClBDSGVhdG1hcChvYmplY3QgPSB0aXNzLCBwYy51c2UgPSAxOjMsIGNlbGxzLnVzZSA9IDUwMCwgZG8uYmFsYW5jZWQgPSBUUlVFLCBsYWJlbC5jb2x1bW5zID0gRkFMU0UsIG51bS5nZW5lcyA9IDgpCmBgYAoKTGF0ZXIgb24gKGluIEZpbmRDbHVzdGVycyBhbmQgVFNORSkgeW91IHdpbGwgcGljayBhIG51bWJlciBvZiBwcmluY2lwYWwgY29tcG9uZW50cyB0byB1c2UuIFRoaXMgaGFzIHRoZSBlZmZlY3Qgb2Yga2VlcGluZyB0aGUgbWFqb3IgZGlyZWN0aW9ucyBvZiB2YXJpYXRpb24gaW4gdGhlIGRhdGEgYW5kLCBpZGVhbGx5LCBzdXByZXNzaW5nIG5vaXNlLiBUaGVyZSBpcyBubyBjb3JyZWN0IGFuc3dlciB0byB0aGUgbnVtYmVyIHRvIHVzZSwgYnV0IGEgZGVjZW50IHJ1bGUgb2YgdGh1bWIgaXMgdG8gZ28gdW50aWwgdGhlIHBsb3QgcGxhdGVhdXMuCgpgYGB7cn0KUENFbGJvd1Bsb3Qob2JqZWN0ID0gdGlzcykKYGBgCgpDaG9vc2UgdGhlIG51bWJlciBvZiBwcmluY2lwYWwgY29tcG9uZW50cyB0byB1c2UuCmBgYHtyfQojIFNldCBudW1iZXIgb2YgcHJpbmNpcGFsIGNvbXBvbmVudHMuIApuLnBjcyA9IDEwCmBgYAoKClRoZSBjbHVzdGVyaW5nIGlzIHBlcmZvcm1lZCBiYXNlZCBvbiBhIG5lYXJlc3QgbmVpZ2hib3JzIGdyYXBoLiBDZWxscyB0aGF0IGhhdmUgc2ltaWxhciBleHByZXNzaW9uIHdpbGwgYmUgam9pbmVkIHRvZ2V0aGVyLiBUaGUgTG91dmFpbiBhbGdvcml0aG0gbG9va3MgZm9yIGdyb3VwcyBvZiBjZWxscyB3aXRoIGhpZ2ggbW9kdWxhcml0eS0tbW9yZSBjb25uZWN0aW9ucyB3aXRoaW4gdGhlIGdyb3VwIHRoYW4gYmV0d2VlbiBncm91cHMuIFRoZSByZXNvbHV0aW9uIHBhcmFtZXRlciBkZXRlcm1pbmVzIHRoZSBzY2FsZS4gSGlnaGVyIHJlc29sdXRpb24gd2lsbCBnaXZlIG1vcmUgY2x1c3RlcnMsIGxvd2VyIHJlc29sdXRpb24gd2lsbCBnaXZlIGZld2VyLgoKRm9yIHRoZSB0b3AtbGV2ZWwgY2x1c3RlcmluZywgYWltIHRvIHVuZGVyLWNsdXN0ZXIgaW5zdGVhZCBvZiBvdmVyLWNsdXN0ZXIuIEl0IHdpbGwgYmUgZWFzeSB0byBzdWJzZXQgZ3JvdXBzIGFuZCBmdXJ0aGVyIGFuYWx5emUgdGhlbSBiZWxvdy4KCmBgYHtyfQojIFNldCByZXNvbHV0aW9uIApyZXMudXNlZCA8LSAwLjUKCnRpc3MgPC0gRmluZENsdXN0ZXJzKG9iamVjdCA9IHRpc3MsIHJlZHVjdGlvbi50eXBlID0gInBjYSIsIGRpbXMudXNlID0gMTpuLnBjcywgCiAgICByZXNvbHV0aW9uID0gcmVzLnVzZWQsIHByaW50Lm91dHB1dCA9IDAsIHNhdmUuU05OID0gVFJVRSkKYGBgCgpXZSB1c2UgVFNORSBzb2xlbHkgdG8gdmlzdWFsaXplIHRoZSBkYXRhLgoKYGBge3J9CiMgSWYgY2VsbHMgYXJlIHRvbyBzcHJlYWQgb3V0LCB5b3UgY2FuIHJhaXNlIHRoZSBwZXJwbGV4aXR5LiBJZiB5b3UgaGF2ZSBmZXcgY2VsbHMsIHRyeSBhIGxvd2VyIHBlcnBsZXhpdHkgKGJ1dCBuZXZlciBsZXNzIHRoYW4gMTApLgp0aXNzIDwtIFJ1blRTTkUob2JqZWN0ID0gdGlzcywgZGltcy51c2UgPSAxOm4ucGNzLCBzZWVkLnVzZSA9IDEwLCBwZXJwbGV4aXR5PTMwKQpgYGAKCmBgYHtyfQpUU05FUGxvdChvYmplY3QgPSB0aXNzLCBkby5sYWJlbCA9IFQsIHB0LnNpemUgPSAxLjIsIGxhYmVsLnNpemUgPSA0KQpgYGAKCkNvbG9yIGJ5IG1ldGFkYXRhLCBsaWtlIHBsYXRlIGJhcmNvZGUsIHRvIGNoZWNrIGZvciBiYXRjaCBlZmZlY3RzLgpgYGB7cn0KVFNORVBsb3Qob2JqZWN0ID0gdGlzcywgZG8ucmV0dXJuID0gVFJVRSwgZ3JvdXAuYnkgPSAicGxhdGUuYmFyY29kZSIpCmBgYAoKCkNoZWNrIGV4cHJlc3Npb24gb2YgZ2VuZXMgdXNlZnVsIGZvciBpbmRpY2F0aW5nIGNlbGwgdHlwZS4KCmBgYHtyLCBlY2hvPUZBTFNFLCBmaWcuaGVpZ2h0PTEyLCBmaWcud2lkdGg9MTJ9CmdlbmVzX211c2NsZV9zdGVtID0gYygnTXlmNScsJ0NhbGNyJywgJ1BheDcnLCAnTXlvZDEnKQpnZW5lc19lbmRvID0gYygnUGVjYW0xJywgJ0F0eG4xJykKZ2VuZXNfaW1tdW5lID0gYygnUHRwcmMnLCAnQ2QzZycsJ0NkNCcsJ0NkOGEnLCdDZDE5JywgJ0l0Z2FtJykKZ2VuZXNfb3RoZXIgPSBjKCdWY2FtMScsICdDaG9kbCcsICdBY3RhMicsICdQZGdmcmEnKQoKZ2VuZXNfdG9fY2hlY2sgPSBjKGdlbmVzX211c2NsZV9zdGVtLCBnZW5lc19lbmRvLCBnZW5lc19pbW11bmUsIGdlbmVzX290aGVyKQpgYGAKCkRvdHBsb3RzIGxldCB5b3Ugc2VlIHRoZSBpbnRlbnNpdHkgb2YgZXhwcmVzc2lvbiBhbmQgdGhlIGZyYWN0aW9uIG9mIGNlbGxzIGV4cHJlc3NpbmcgZm9yIGVhY2ggb2YgeW91ciBnZW5lcyBvZiBpbnRlcmVzdC4KVGhlIHJhZGl1cyBzaG93cyB5b3UgdGhlIHBlcmNlbnQgb2YgY2VsbHMgaW4gdGhhdCBjbHVzdGVyIHdpdGggYXQgbGVhc3Qgb25lIHJlYWQgc2VxdWVuY2VkIGZyb20gdGhhdCBnZW5lLiBUaGUgY29sb3IgbGV2ZWwgaW5kaWNhdGVzIHRoZSBhdmVyYWdlIFotc2NvcmUgb2YgZ2VuZSBleHByZXNzaW9uIGZvciBjZWxscyBpbiB0aGF0IGNsdXN0ZXIsIHdoZXJlIHRoZSBzY2FsaW5nIGlzIGRvbmUgb3ZlciBhbGwgY2VsbHMgaW4gdGhlIHNhbXBsZS4KCmBgYHtyLCBlY2hvPUZBTFNFLCBmaWcuaGVpZ2h0PTMsIGZpZy53aWR0aD0zfQpEb3RQbG90KHRpc3MsIGdlbmVzX3RvX2NoZWNrLCBwbG90LmxlZ2VuZCA9IFQsIGNvbC5tYXggPSAyLjUsIGRvLnJldHVybiA9IFQpICsgY29vcmRfZmxpcCgpCmBgYAoKYGBge3IsIGVjaG89RkFMU0UsIGZpZy5oZWlnaHQ9MTIsIGZpZy53aWR0aD0xMn0KRmVhdHVyZVBsb3QodGlzcywgZ2VuZXNfdG9fY2hlY2ssIHB0LnNpemUgPSAxLCBuQ29sID0gNSwgY29scy51c2UgPSBjKCJncmV5IiwgInJlZCIpKQpgYGAKCgpXZSBjYW4gYWxzbyBmaW5kIGFsbCBkaWZmZXJlbnRpYWxseSBleHByZXNzZWQgZ2VuZXMgbWFya2luZyBlYWNoIGNsdXN0ZXIuIFRoaXMgbWF5IHRha2Ugc29tZSB0aW1lLgoKYGBge3J9CiNjbHVzdC5tYXJrZXJzMCA8LSBGaW5kTWFya2VycyhvYmplY3QgPSB0aXNzLCBpZGVudC4xID0gMCwgb25seS5wb3MgPSBUUlVFLCBtaW4ucGN0ID0gMC4yNSwgdGhyZXNoLnVzZSA9IDAuMjUpCiN0aXNzLm1hcmtlcnMgPC0gRmluZEFsbE1hcmtlcnMob2JqZWN0ID0gdGlzcywgb25seS5wb3MgPSBUUlVFLCBtaW4ucGN0ID0gMC4yNSwgdGhyZXNoLnVzZSA9IDAuMjUpCmBgYAoKRGlzcGxheSB0aGUgdG9wIG1hcmtlcnMgeW91IGNvbXB1dGVkIGFib3ZlLgpgYGB7cn0KI3Rpc3MubWFya2VycyAlPiUgZ3JvdXBfYnkoY2x1c3RlcikgJT4lIHRvcF9uKDUsIGF2Z19kaWZmKQpgYGAKClVzaW5nIHRoZSBtYXJrZXJzIGFib3ZlLCB3ZSBjYW4gY29uZmlkZW50YWx5IGxhYmVsIG1hbnkgb2YgdGhlIGNsdXN0ZXJzOgoKV2Ugd2lsbCBhZGQgdGhvc2UgY2VsbF9vbnRvbG9neV9jbGFzcyB0byB0aGUgZGF0YXNldC4KCmBgYHtyfQp0aXNzIDwtIFN0YXNoSWRlbnQob2JqZWN0ID0gdGlzcywgc2F2ZS5uYW1lID0gImNsdXN0ZXIuaWRzIikKCmNsdXN0ZXIuaWRzIDwtIGMoMCwgMSwgMiwgMywgNCwgNSwgNikKCmZyZWVfYW5ub3RhdGlvbiA8LSBjKAogICJza2VsZXRhbCBtdXNjbGUgc2F0ZWxsaXRlIGNlbGwiLCAKICAibWVzZW5jaHltYWwgc3RlbSBjZWxsIiwgCiAgInNrZWxldGFsIG11c2NsZSBzYXRlbGxpdGUgY2VsbCIsIAogICJlbmRvdGhlbGlhbCBjZWxsIiwKICAiQiBjZWxsIiwgCiAgIm1hY3JvcGhhZ2UiLAogICJUIGNlbGwiKQoKY2VsbF9vbnRvbG9neV9jbGFzcyA8LWMoCiAgInNrZWxldGFsIG11c2NsZSBzYXRlbGxpdGUgY2VsbCIsIAogICJtZXNlbmNoeW1hbCBzdGVtIGNlbGwiLCAKICAic2tlbGV0YWwgbXVzY2xlIHNhdGVsbGl0ZSBjZWxsIiwgCiAgImVuZG90aGVsaWFsIGNlbGwiLAogICJCIGNlbGwiLCAKICAibWFjcm9waGFnZSIsCiAgIlQgY2VsbCIpCgp2YWxpZGF0ZV9jZWxsX29udG9sb2d5KGNlbGxfb250b2xvZ3lfY2xhc3MpCmNlbGxfb250b2xvZ3lfaWQgPSBjb252ZXJ0X3RvX2NlbGxfb250b2xvZ3lfaWQoY2VsbF9vbnRvbG9neV9jbGFzcykKCnRpc3NAbWV0YS5kYXRhWydmcmVlX2Fubm90YXRpb24nXSA8LSBhcy5jaGFyYWN0ZXIocGx5cjo6bWFwdmFsdWVzKHggPSB0aXNzQGlkZW50LCBmcm9tID0gY2x1c3Rlci5pZHMsIHRvID0gZnJlZV9hbm5vdGF0aW9uKSkKdmFsaWRhdGVfY2VsbF9vbnRvbG9neShjZWxsX29udG9sb2d5X2NsYXNzKQpjZWxsX29udG9sb2d5X2lkID0gY29udmVydF90b19jZWxsX29udG9sb2d5X2lkKGNlbGxfb250b2xvZ3lfY2xhc3MpCgp0aXNzQG1ldGEuZGF0YVsnY2VsbF9vbnRvbG9neV9jbGFzcyddIDwtIGFzLmNoYXJhY3RlcihwbHlyOjptYXB2YWx1ZXMoeCA9IHRpc3NAaWRlbnQsIGZyb20gPSBjbHVzdGVyLmlkcywgdG8gPSBjZWxsX29udG9sb2d5X2NsYXNzKSkKdGlzc0BtZXRhLmRhdGFbJ2NlbGxfb250b2xvZ3lfaWQnXSA8LSBhcy5jaGFyYWN0ZXIocGx5cjo6bWFwdmFsdWVzKHggPSB0aXNzQGlkZW50LCBmcm9tID0gY2x1c3Rlci5pZHMsIHRvID0gY2VsbF9vbnRvbG9neV9pZCkpCmBgYAoKIyMgU3ViY2x1c3RlcgoKYGBge3J9CnN1YnRpc3MgPSBTdWJzZXREYXRhKHRpc3MsIGlkZW50LnVzZSA9IGMoMSkpCmBgYAoKYGBge3J9CnN1YnRpc3MgPC0gc3VidGlzcyAlPiUgU2NhbGVEYXRhKCkgJT4lIAogIEZpbmRWYXJpYWJsZUdlbmVzKGRvLnBsb3QgPSBUUlVFLCB4LmhpZ2guY3V0b2ZmID0gSW5mLCB5LmN1dG9mZiA9IDAuNSkgJT4lCiAgUnVuUENBKGRvLnByaW50ID0gRkFMU0UpCmBgYAoKYGBge3J9ClBDSGVhdG1hcChvYmplY3QgPSBzdWJ0aXNzLCBwYy51c2UgPSAxOjMsIGNlbGxzLnVzZSA9IDUwMCwgZG8uYmFsYW5jZWQgPSBUUlVFLCBsYWJlbC5jb2x1bW5zID0gRkFMU0UsIG51bS5nZW5lcyA9IDgpClBDRWxib3dQbG90KHN1YnRpc3MpCmBgYAoKCmBgYHtyfQpzdWIubi5wY3MgPSAxMApzdWIucmVzLnVzZSA9IDEKc3VidGlzcyA8LSBzdWJ0aXNzICU+JSBGaW5kQ2x1c3RlcnMocmVkdWN0aW9uLnR5cGUgPSAicGNhIiwgZGltcy51c2UgPSAxOnN1Yi5uLnBjcywgCiAgICByZXNvbHV0aW9uID0gc3ViLnJlcy51c2UsIHByaW50Lm91dHB1dCA9IDAsIHNhdmUuU05OID0gVFJVRSwgZm9yY2UucmVjYWxjID0gVFJVRSkgJT4lCiAgICBSdW5UU05FKGRpbXMudXNlID0gMTpzdWIubi5wY3MsIHNlZWQudXNlID0gMTAsIHBlcnBsZXhpdHk9MjApCgpUU05FUGxvdChvYmplY3QgPSBzdWJ0aXNzLCBkby5sYWJlbCA9IFQsIHB0LnNpemUgPSAxLjIsIGxhYmVsLnNpemUgPSA0KQpgYGAKCmBgYHtyfQpUU05FUGxvdChzdWJ0aXNzLCBncm91cC5ieSA9ICJtb3VzZS5pZCIpCmBgYAoKYGBge3IsIGVjaG89RkFMU0UsIGZpZy5oZWlnaHQ9MTIsIGZpZy53aWR0aD04fQpGZWF0dXJlUGxvdChzdWJ0aXNzLCBnZW5lc190b19jaGVjaykKYGBgCgpgYGB7ciwgZWNobz1GQUxTRSwgZmlnLmhlaWdodD04LCBmaWcud2lkdGg9MTB9CkRvdFBsb3Qoc3VidGlzcywgZ2VuZXNfdG9fY2hlY2ssIGNvbC5tYXggPSAyLjUsIHBsb3QubGVnZW5kID0gVCwgZG8ucmV0dXJuID0gVCkgKyBjb29yZF9mbGlwKCkKYGBgCgpgYGB7cn0KdGFibGUoRmV0Y2hEYXRhKHN1YnRpc3MsIGMoJ21vdXNlLmlkJywnaWRlbnQnKSkgJT4lIGRyb3BsZXZlbHMoKSkKYGBgCgpgYGB7cn0KIyBzdWIuY2x1c3Rlci5pZHMgPC0gYygwLCAxLCAyLCAzLCA0LCA1LCA2LCA3KQojIHN1Yi5mcmVlX2Fubm90YXRpb24gPC0gYygicGFuY3JlYXRpYyBBIGNlbGwiLCAicGFuY3JlYXRpYyBBIGNlbGwiLCAicGFuY3JlYXRpYyBBIGNlbGwiLCAicGFuY3JlYXRpYyBEIGNlbGwiLCAicGFuY3JlYXRpYyBBIGNlbGwiLCAicGFuY3JlYXRpYyBEIGNlbGwiLCAicGFuY3JlYXRpYyBQUCBjZWxsIiwgInBhbmNyZWF0aWMgUFAgY2VsbCIpCiMgc3ViLmNlbGxfb250b2xvZ3lfY2xhc3MgPC1jKCJwYW5jcmVhdGljIEEgY2VsbCIsICJwYW5jcmVhdGljIEEgY2VsbCIsICJwYW5jcmVhdGljIEEgY2VsbCIsICJwYW5jcmVhdGljIEQgY2VsbCIsICJwYW5jcmVhdGljIEEgY2VsbCIsICJwYW5jcmVhdGljIEQgY2VsbCIsICJwYW5jcmVhdGljIFBQIGNlbGwiLCAicGFuY3JlYXRpYyBQUCBjZWxsIikKIyAKIyB2YWxpZGF0ZV9jZWxsX29udG9sb2d5KHN1Yi5jZWxsX29udG9sb2d5X2NsYXNzKQojIHN1Yi5jZWxsX29udG9sb2d5X2lkID0gY29udmVydF90b19jZWxsX29udG9sb2d5X2lkKHN1Yi5jZWxsX29udG9sb2d5X2NsYXNzKQojIAojIHN1YnRpc3NAbWV0YS5kYXRhWydmcmVlX2Fubm90YXRpb24nXSA8LSBhcy5jaGFyYWN0ZXIocGx5cjo6bWFwdmFsdWVzKHggPSBzdWJ0aXNzQGlkZW50LCBmcm9tID0gc3ViLmNsdXN0ZXIuaWRzLCB0byA9IHN1Yi5mcmVlX2Fubm90YXRpb24pKQojIHZhbGlkYXRlX2NlbGxfb250b2xvZ3koc3ViLmNlbGxfb250b2xvZ3lfY2xhc3MpCiMgc3ViLmNlbGxfb250b2xvZ3lfaWQgPSBjb252ZXJ0X3RvX2NlbGxfb250b2xvZ3lfaWQoc3ViLmNlbGxfb250b2xvZ3lfY2xhc3MpCiMgCiMgc3VidGlzc0BtZXRhLmRhdGFbJ2ZyZWVfYW5ub3RhdGlvbiddIDwtIGFzLmNoYXJhY3RlcihwbHlyOjptYXB2YWx1ZXMoeCA9IHN1YnRpc3NAaWRlbnQsIGZyb20gPSBzdWIuY2x1c3Rlci5pZHMsIHRvID0gc3ViLmZyZWVfYW5ub3RhdGlvbikpCiMgc3VidGlzc0BtZXRhLmRhdGFbJ2NlbGxfb250b2xvZ3lfY2xhc3MnXSA8LSBhcy5jaGFyYWN0ZXIocGx5cjo6bWFwdmFsdWVzKHggPSBzdWJ0aXNzQGlkZW50LCBmcm9tID0gc3ViLmNsdXN0ZXIuaWRzLCB0byA9IHN1Yi5jZWxsX29udG9sb2d5X2NsYXNzKSkKIyBzdWJ0aXNzQG1ldGEuZGF0YVsnY2VsbF9vbnRvbG9neV9pZCddIDwtIGFzLmNoYXJhY3RlcihwbHlyOjptYXB2YWx1ZXMoeCA9IHN1YnRpc3NAaWRlbnQsIGZyb20gPSBzdWIuY2x1c3Rlci5pZHMsIHRvID0gc3ViLmNlbGxfb250b2xvZ3lfaWQpKQpgYGAKCiMjIEFkZCBzdWJjbHVzdGVyIGNlbGxfb250b2xvZ3lfY2xhc3MgdG8gbWFpbiBjZWxsX29udG9sb2d5X2NsYXNzCgpgYGB7cn0KIyBzdWIuY2VsbHMgPSByb3duYW1lcyhzdWJ0aXNzQG1ldGEuZGF0YSkKIyAKIyB0aXNzQG1ldGEuZGF0YVtzdWIuY2VsbHMsICdmcmVlX2Fubm90YXRpb24nXSA9IHN1YnRpc3NAbWV0YS5kYXRhWywnZnJlZV9hbm5vdGF0aW9uJ10KIyB0aXNzQG1ldGEuZGF0YVtzdWIuY2VsbHMsICdjZWxsX29udG9sb2d5X2NsYXNzJ10gPSBzdWJ0aXNzQG1ldGEuZGF0YVssJ2NlbGxfb250b2xvZ3lfY2xhc3MnXQojIHRpc3NAbWV0YS5kYXRhW3N1Yi5jZWxscywgJ2NlbGxfb250b2xvZ3lfaWQnXSA9IHN1YnRpc3NAbWV0YS5kYXRhWywnY2VsbF9vbnRvbG9neV9pZCddCmBgYAoKIyBGaW5hbCBjb2xvcmluZwoKQ29sb3IgYnkgY2VsbCBvbnRvbG9neSBjbGFzcyBvbiB0aGUgb3JpZ2luYWwgVFNORS4KCmBgYHtyfQpUU05FUGxvdChvYmplY3QgPSB0aXNzLCBkby5yZXR1cm4gPSBUUlVFLCBncm91cC5ieSA9ICJjZWxsX29udG9sb2d5X2NsYXNzIikKYGBgCgojIFNhdmUgdGhlIFJvYmplY3QgZm9yIGxhdGVyCgpgYGB7cn0KZmlsZW5hbWUgPSBoZXJlKCcwMF9kYXRhX2luZ2VzdCcsICcwNF90aXNzdWVfcm9ial9nZW5lcmF0ZWQnLCAKICAgICAgICAgICAgICAgICAgICAgcGFzdGUwKCJmYWNzXyIsIHRpc3N1ZV9vZl9pbnRlcmVzdCwgIl9zZXVyYXRfdGlzcy5Sb2JqIikpCnByaW50KGZpbGVuYW1lKQpzYXZlKHRpc3MsIGZpbGU9ZmlsZW5hbWUpCmBgYAoKYGBge3J9CiMgVG8gcmVsb2FkIGEgc2F2ZWQgb2JqZWN0CiMgZmlsZW5hbWUgPSBoZXJlKCcwMF9kYXRhX2luZ2VzdCcsICcwNF90aXNzdWVfcm9ial9nZW5lcmF0ZWQnLCAKIyAgICAgICAgICAgICAgICAgICAgICBwYXN0ZTAoImZhY3MiLCB0aXNzdWVfb2ZfaW50ZXJlc3QsICJfc2V1cmF0X3Rpc3MuUm9iaiIpKQojIGxvYWQoZmlsZT1maWxlbmFtZSkKYGBgCgoKIyBFeHBvcnQgdGhlIGZpbmFsIG1ldGFkYXRhCgpTbyB0aGF0IEJpb2h1YiBjYW4gZWFzaWx5IGNvbWJpbmUgYWxsIHlvdXIgYW5ub3RhdGlvbnMsIHBsZWFzZSBleHBvcnQgdGhlbSBhcyBhIHNpbXBsZSBjc3YuCgpgYGB7cn0KaGVhZCh0aXNzQG1ldGEuZGF0YSkKYGBgCgpgYGB7cn0KZmlsZW5hbWUgPSBoZXJlKCcwMF9kYXRhX2luZ2VzdCcsICcwM190aXNzdWVfYW5ub3RhdGlvbl9jc3YnLCAKICAgICAgICAgICAgICAgICAgICAgcGFzdGUwKHRpc3N1ZV9vZl9pbnRlcmVzdCwgIl9hbm5vdGF0aW9uLmNzdiIpKQp3cml0ZS5jc3YoRmV0Y2hEYXRhKHRpc3MsIGMoJ3BsYXRlLmJhcmNvZGUnLCdjZWxsX29udG9sb2d5X2NsYXNzJywnY2VsbF9vbnRvbG9neV9pZCcsICdmcmVlX2Fubm90YXRpb24nLCAndFNORV8xJywgJ3RTTkVfMicpKSwgZmlsZT1maWxlbmFtZSkKYGBgCkZpZ3VyZXMgZm9yIFN1cHBsZW1lbnQuIERvdCBwbG90IGFib3ZlIGlzIHN1ZmZpY2llbnQgYW5kIGNvZGUgaXMgY29waWVkIGJlbG93LgpgYGB7ciwgZWNobz1GQUxTRSwgZmlnLmhlaWdodD0zLCBmaWcud2lkdGg9M30KRG90UGxvdCh0aXNzLCBnZW5lc190b19jaGVjaywgcGxvdC5sZWdlbmQgPSBULCBjb2wubWF4ID0gMi41LCBkby5yZXR1cm4gPSBUKSArIGNvb3JkX2ZsaXAoKQpgYGAKYGBgCg==